﻿// --------------------------------------------------------------------------------------------------------------------
// <summary>
//   XPathMsmqValidationStep.cs 
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace TRlib.BizUnit.CustomSteps
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Messaging;
    using System.Text;
    using System.Threading;
    using System.Xml;
    using global::BizUnit;

    /// <summary>
    /// The XPathMsmqValidationStep reads a MSMQ message from a given queue and validates the contents.
    /// </summary>
    /// 
    /// <remarks>
    /// The following shows an example of the Xml representation of this test step.
    /// 
    /// <code escaped="true">
    ///       <TestStep assemblyPath="" typeName="MSMQReadStep">
    ///        <QueuePath>.\Private$\Test01</QueuePath>
    ///        <Timeout>2000</Timeout>
    ///        <MessageIndexOnQueue>2</MessageIndexOnQueue>
    /// 
    ///        <XPathList>
    ///            <XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='PONumber' and namespace-uri()='']" matchType="ContainsOnly">PONumber_0</XPathValidation>
    ///            <XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='OtherField' and namespace-uri()='']" takeFromCtx="expectedValue" queryType="Count" />
    ///        </XPathList>
    ///                    
    ///    </TestStep>
    ///    </code>
    ///    
    ///    <list type="table">
    ///        <listheader>
    ///            <term>Tag</term>
    ///            <description>Description</description>
    ///        </listheader>
    ///        <item>
    ///            <term>QueuePath</term>
    ///            <description>The MSMQ queue to read a message from</description>
    ///        </item>
    ///        <item>
    ///            <term>Timeout</term>
    ///            <description>The timeout to wait for the message to appear in the queue, in milisecs</description>
    ///        </item>
    ///        <item>
    ///            <term>MessageIndexOnQueue</term>
    ///            <description>If the message you are interested in is not the first message, you can specify which position (eg 1,2,3) it is at on the queue</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathList</term>
    ///            <description>Container for one or more &lt;XPathValidation&gt; tags</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation</term>
    ///            <description>Specifies a query and expected result for the query. The result is optional if using @takeFromCtx (see below)</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@query</term>
    ///            <description>Required. An xpath expresion</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@takeFromCtx</term>
    ///            <description>Optional. Allows you to specify the expected result from the context by supplying a context key</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@matchType</term>
    ///            <description>Optional. Specifies the type of match. Supported values: Exact, ContainsOnly, RegEx, defaults to Exact</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@queryType</term>
    ///            <description>Optional. Specifies the type of query. Supported values: Node, Count, defaults to Node</description>
    ///        </item>
    ///    </list>
    /// </remarks>
    public class XPathMsmqValidationStep : Validator, ITestStep
    {
        #region ITestStep Members
        public void Execute(XmlNode testConfig, Context context)
        {
            // Read config
            string queuePath = context.ReadConfigAsString(testConfig, "QueuePath");
            double timeout = context.ReadConfigAsDouble(testConfig, "Timeout");
            XmlNodeList xpathChecks = testConfig.SelectNodes("//XPathList/*");

            // The following is taken directly from the BizUnit MsmqReadStep, hence no comments.
            MemoryStream data = null;

            try
            {
                context.LogInfo("MSMQReadStep about to read data from queue: {0}", queuePath);

                // Get message index
                int messageIndex = 0;
                try
                {
                    messageIndex = context.ReadConfigAsInt32(testConfig, "MessagePositionOnQueue");
                }
                catch (NullReferenceException)
                {
                    // Continue if cannot find the node.
                }     
                
                if (messageIndex == 0)
                {
                    messageIndex = 1;
                }

                for (int i = 0; i < messageIndex; i++)
                {
                    using (var queue = new MessageQueue(queuePath))
                    {
                        Message msg = queue.Receive(TimeSpan.FromMilliseconds(timeout), MessageQueueTransactionType.Single);

                        // Dump msg content to console
                        data = StreamHelper.LoadMemoryStream(msg.BodyStream);
                        StreamHelper.WriteStreamToConsole("MSMQ message data", data, context);
                        data.Seek(0, SeekOrigin.Begin);

                        // If this is the queue position specified
                        if (i + 1 == messageIndex)
                        {
                            // Call Validate()
                            this.Validate(data, xpathChecks, context);
                        }
                        else
                        {
                            context.LogInfo("Dequeued but skipped message at position {0} on queue.", i + 1);
                        }
                    }
                }
            }
            finally
            {
                if (null != data)
                {
                    data.Close();
                }
            }

            // end: Taken from BizUnit MsmqReadStep
        }
        #endregion
    }
}
